﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.ComponentModel;
using System.Collections.Generic;

namespace Oliver.Controls
{

    public class FishEyeMenu : BaseContainer
    {
        public FishEyeMenu()
        {
            this.MouseMove +=new MouseEventHandler(FishEyeMenu_MouseMove);
            this.MouseLeave += new MouseEventHandler(FishEyeMenu_MouseLeave);
        }

        #region Properties

        private static double C_MARGIN = 15;			// Margin between images
        private double _itemMargin = C_MARGIN;

        public double ItemMargin
        {
            get { return _itemMargin; }
            set
            {
                _itemMargin = value;
                this.InvalidateArrange();
            }
        }

        private static double C_ITEM_WIDTH = 48;	// Image width
        private double _itemWidth = C_ITEM_WIDTH;
        public double ItemWidth
        {
            get { return _itemWidth; }
            set
            {
                _itemWidth = value;
                this.InvalidateArrange();
            }
        }

        private static double C_ITEM_HEIGHT = 48;
        private double _itemHeight = C_ITEM_HEIGHT;
        public double ItemHeight
        {
            get { return _itemHeight; }
            set
            {
                _itemHeight = value;
                this.InvalidateArrange();
            }
        }

        private static double C_MAX_SCALE = 3;
        private double _maxScale = C_MAX_SCALE;
        public double MaxScale
        {
            get { return _maxScale; }
            set
            {
                _maxScale = value;
                this.InvalidateArrange();
            }
        }

        private static double C_MULTIPLIER = 60;
        private double _multiplier = C_MULTIPLIER;
        public double Multiplier
        {
            get { return _multiplier; }
            set
            {
                _multiplier = value;
                this.InvalidateArrange();
            }
        }

        private Orientation _orientation = Orientation.Horizontal;
        public Orientation Orientation
        {
            get { return _orientation; }
            set { _orientation = value; }
        }

        private bool _absolutePositioning = true;
        public bool AbsolutePositioning
        {
            get { return _absolutePositioning; }
            set { _absolutePositioning = value; }
        }

        #endregion

        #region MouseEvents

        void FishEyeMenu_MouseLeave(object sender, MouseEventArgs e)
        {
            for (int i = 0; i < this.Children.Count; i++)
            {
                FrameworkElement element = this.Children[i] as FrameworkElement;

                double imageScale = 1; 
                ScaleElement(element, imageScale);

                element.SetValue(Canvas.ZIndexProperty, (int)Math.Round(this.ItemWidth * imageScale));
            }
        }

        void  FishEyeMenu_MouseMove(object sender, MouseEventArgs e)
        {
            ApplyLayout(e.GetPosition(this));
        }

        private void ApplyLayout(Point mousePosition)
        {
            List<double> scaleFactors = new List<double>();

            for (int i = 0; i < this.Children.Count; i++)
            {
                UIElement element = this.Children[i];

                if (_absolutePositioning)
                {
                    if (Orientation == Orientation.Horizontal)
                    {
                        double left = i * (ItemWidth + ItemMargin);
                        double currentXPosition = Math.Abs(mousePosition.X - (left + ItemWidth / 2));
                        double imageScale = this.MaxScale - Math.Min(this.MaxScale - 1, currentXPosition / this.Multiplier);
                        element.SetValue(Canvas.ZIndexProperty, (int)Math.Round(this.ItemWidth * imageScale));
                        scaleFactors.Add(imageScale);
                    }
                    else
                    {
                        double top = i * (ItemHeight + ItemMargin);
                        double currentYPosition = Math.Abs(mousePosition.Y - (top + ItemHeight / 2));
                        double imageScale = this.MaxScale - Math.Min(this.MaxScale - 1, currentYPosition / this.Multiplier);
                        element.SetValue(Canvas.ZIndexProperty, (int)Math.Round(this.ItemHeight * imageScale));

                        scaleFactors.Add(imageScale);
                    }
                }
                else
                {
                    #region underconstruction

                    if (Orientation == Orientation.Horizontal)
                    {
                        double left = 0;
                        for (int j = 0; j < i; j++)
                        {
                            left += ItemWidth * scaleFactors[j];
                        }

                        double currentXPosition = Math.Abs(mousePosition.X - (left + ItemWidth / 2));
                        double imageScale = this.MaxScale - Math.Min(this.MaxScale - 1, currentXPosition / this.Multiplier);
                        scaleFactors.Add(imageScale);
                        
                        element.SetValue(Canvas.ZIndexProperty, (int)Math.Round(this.ItemWidth * imageScale));
                        
                    }
                    else
                    {
                        double top = i * (ItemHeight + ItemMargin);
                        double currentYPosition = Math.Abs(mousePosition.Y - (top + ItemHeight / 2));
                        double imageScale = this.MaxScale - Math.Min(this.MaxScale - 1, currentYPosition / this.Multiplier);
                        element.SetValue(Canvas.ZIndexProperty, (int)Math.Round(this.ItemHeight * imageScale));

                        scaleFactors.Add(imageScale);
                    }

                    #endregion
                }
            }

            _scaleFactors = scaleFactors.ToArray();

            for (int i = 0; i < this.Children.Count; i++)
            {
                UIElement element = Children[i];
                ScaleElement(element, _scaleFactors[i]);
            }
        }

        #endregion

        #region Scale Method

        double[] _scaleFactors;

        private void ScaleElement(UIElement element, double scaleFactor)
        {
            if (scaleFactor <= 0 || double.IsNaN(scaleFactor))
            {
                return;
            }

            ScaleTransform trans = null;
            if (element.RenderTransform is TransformGroup)
            {
                TransformGroup tg = element.RenderTransform as TransformGroup;
                foreach (Transform t in tg.Children)
                {
                    if (t is ScaleTransform)
                    {
                        trans = t as ScaleTransform;
                    }
                }
            }

            if (trans == null)
            {
                trans = new ScaleTransform();
                element.RenderTransform = trans;
            }

            trans.CenterX = ItemWidth / 2;
            trans.CenterY = ItemHeight / 2;
            trans.ScaleX = scaleFactor;
            trans.ScaleY = scaleFactor;

            if (!_absolutePositioning)
            {
                double left = 0;
                double top = 0;
                int elementId = 0;
                for (int i = 0; i < Children.Count; i++)
                {
                    elementId = i;
                    if (element == Children[i])
                        break;

                    left += _scaleFactors[i] * (ItemWidth * 1.5 + ItemMargin);
                }
                top = ItemHeight * MaxScale / 2;

                Rect rect = new Rect(left, top, ItemWidth * _scaleFactors[elementId], ItemHeight * _scaleFactors[elementId]);
                element.Arrange(rect);
            }
        }

        #endregion

        #region Overrides

        //protected override Size MeasureOverride(Size availableSize)
        //{
        //    foreach (UIElement child in this.Children)
        //    {
        //        child.Measure(new Size(ItemWidth, ItemHeight));
        //    }
        //    return base.MeasureOverride(availableSize);
        //}

        protected override Size ArrangeOverride(Size finalSize)
        {
            double top = 0;
            double left = 0;

            for (int i = 0; i < this.Children.Count; i++)
            {
                UIElement element = this.Children[i];

                if (this.Orientation == Orientation.Horizontal)
                {
                    double t = ItemHeight * MaxScale / 2;
                    double l = left + i * this.ItemMargin;

                    left += this.ItemWidth + this.ItemMargin;

                    Rect rect = new Rect(l, t, ItemWidth, ItemHeight);
                    element.Arrange(rect);
                }
                else
                {
                    double l = ItemWidth * MaxScale / 2;
                    double t = top + i * this.ItemMargin;

                    top += this.ItemHeight + this.ItemMargin;

                    Rect rect = new Rect(l, t, ItemWidth, ItemHeight);
                    element.Arrange(rect);
                }
            }

            double width = 0;
            double height = 0;

            if (Orientation == Orientation.Horizontal)
            {
                width = left + this.ItemWidth;
                height = top + this.ItemHeight * MaxScale;
            }
            else
            {
                width = left + this.ItemWidth * MaxScale;
                height = top + this.ItemHeight;
            }

            Size newFinalSize = new Size(width, height);
            return base.ArrangeOverride(newFinalSize);
        }

        #endregion
    }
     
}